﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Data;
using RootfusHostView;

namespace RoofusUIControls
{
    /// <summary>
    /// Connects two surface items together -- to show data flow!
    /// </summary>
    public class SurfaceItemConnector : DependencyObject
    {
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
            "Source",
            typeof(SurfaceItem),
            typeof(SurfaceItemConnector),
            new PropertyMetadata(null, new PropertyChangedCallback(OnSourceChanged)));

        /// <summary>
        /// Get/Set the source property for this connector.
        /// </summary>
        public SurfaceItem Source
        {
            get { return (SurfaceItem)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        /// <summary>
        /// Fired when Sink changes.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        private static void OnSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            SurfaceItemConnector cont = obj as SurfaceItemConnector;
            if (cont == null)
            {
                return;
            }

            ///
            /// Remove the connection if need be
            ///

            UnconfigureSourceSink(cont, e.OldValue as SurfaceItem, cont.Sink);

            ///
            /// Create the connection if need be.
            /// 

            ConfigureSourceSink(cont, cont.Source, cont.Sink);

            ///
            /// Update the connection (Draw it, for example).
            ///

            cont.Update();
        }

        public static readonly DependencyProperty SinkProperty = DependencyProperty.Register(
            "Sink",
            typeof(SurfaceItem),
            typeof(SurfaceItemConnector),
            new PropertyMetadata(null, new PropertyChangedCallback(OnSinkChanged)));

        /// <summary>
        /// Get/Set the sink property for this connector.
        /// </summary>
        public SurfaceItem Sink
        {
            get { return (SurfaceItem)GetValue(SinkProperty); }
            set { SetValue(SinkProperty, value); }
        }

        /// <summary>
        /// Fired when Sink changes.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        private static void OnSinkChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            SurfaceItemConnector cont = obj as SurfaceItemConnector;
            if (cont == null) {
                return;
            }

            ///
            /// If we are changing, kill off the old guy
            /// 

            UnconfigureSourceSink(cont, cont.Source, e.OldValue as SurfaceItem);
            
            ///
            /// Then add it!
            /// 

            ConfigureSourceSink(cont, cont.Source, cont.Sink);
            cont.Update();
        }

        /// <summary>
        /// Does the configure of the source and sink.
        /// </summary>
        static void ConfigureSourceSink(SurfaceItemConnector cont, SurfaceItem source, SurfaceItem sink)
        {
            if (source == null || sink == null)
            {
                return;
            }
            sink.AddIncomingConnection(cont);
            source.Wrapper.DownstreamConnectedSurfaceItems.Add(sink.Wrapper);
        }

        /// <summary>
        /// Unconfigure the source and sink.
        /// </summary>
        /// <param name="cont"></param>
        static void UnconfigureSourceSink(SurfaceItemConnector cont, SurfaceItem source, SurfaceItem sink)
        {
            if (source == null || sink == null)
            {
                return;
            }
            sink.RemoveIncomingConnection(cont, source);
            source.Wrapper.DownstreamConnectedSurfaceItems.Remove(sink.Wrapper);
        }

        public static readonly DependencyProperty HostProperty = DependencyProperty.Register(
            "Host",
            typeof(RootfusProgrammingSurface),
            typeof(SurfaceItemConnector),
            new PropertyMetadata(null, new PropertyChangedCallback(OnHostChanged)));

        /// <summary>
        /// Get/Set the source property for this connector.
        /// </summary>
        public RootfusProgrammingSurface Host
        {
            get { return (RootfusProgrammingSurface)GetValue(HostProperty); }
            set { SetValue(HostProperty, value); }
        }

        /// <summary>
        /// Fired when Host changes.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        private static void OnHostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            SurfaceItemConnector cont = obj as SurfaceItemConnector;
            cont.Update();
        }

        /// <summary>
        /// The line that is drawn between the two surface items.
        /// </summary>
        private ObjectCenterLine _connector_line = null;

        /// <summary>
        /// Update the display.
        /// </summary>
        public void Update()
        {
            /// Check to see if we have enough information to do it.
            if (Host != null
                && Source != null
                && Sink != null)
            {
                if (_connector_line == null)
                {
                    _connector_line = new ObjectCenterLine();
                }

                _connector_line.Host = Host.dragSurface;
                _connector_line.LineStartObject = Source.rectOutputPins;
                _connector_line.LineFinishObject = Sink.rectInputPins;
                _connector_line.LineSourceMoveMonitor = Source;
                _connector_line.LineSinkMoveMonitor = Sink;
            }
            else
            {
                if (_connector_line != null)
                {
                    _connector_line.Host = null;
                    _connector_line = null;
                }
            }
        }

        /// <summary>
        /// The name of this guy -- used to show the user what the incoming connection is.
        /// </summary>
        public string SourceName
        {
            get { return Source.Wrapper.UserName; }
        }

        /// <summary>
        /// Returns the output pins that are used by the source.
        /// </summary>
        public IList<string> SourceOutputPins
        {
            get { return Source.OutputPins; }
        }
    }
}
